package org.rishi.framework.db;

import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author MHT E-mail:mht1984@126.com
 * @version 创建时间：2010-8-31 下午05:04:30
 *
 */
class YAMLConfigParser {

    public static final String file = "META-INF/db.yaml";
    private static Logger logger = LoggerFactory.getLogger(YAMLConfigParser.class);
    private static List lines = new ArrayList();
    private static List indents = new ArrayList();
    private static String[] cnAttribs = new String[]{
            "name",
            "datasource",
            "url",
            "driver",
            "uid",
            "passwd"
    };
    static List config = new ArrayList();

    static {
    }

    List holders = new ArrayList();

    YAMLConfigParser() {
    }

    void init() throws Throwable {
        try {
            parse();
            filterComments();

            for (int i = 0; i < lines.size(); i++) {
                logger.trace("line#" + (i) + ": (" + indents.get(i) + ") "
                        + lines.get(i));
            }
            parseConnections();
            parseDatePatterns();
            parseClassEntityHash();
            parseNamedQueries();
            parseColMappings();

        } catch (Exception e) {
            logger.info("db.yaml file not found");
            // e.printStackTrace();
            throw e;
        }

    }

    private void addConnections() {
        for (int i = 0; i < holders.size(); i++) {
            if (holders.get(i) instanceof ConnectionData) {
                ConnectionData conn = (ConnectionData) holders.get(i);
                if (conn.getName() != null) {
                    if (conn.getDatasource() == null) {
                        List connParams = new ArrayList();
                        connParams.add(conn.getDriver());
                        connParams.add(conn.getUrl());
                        connParams.add(conn.getUid());
                        String pwd = (conn.getPasswd() == null) ? "" : conn
                                .getPasswd();
                        connParams.add(pwd);
                        DAOImpl.connMap.put(conn.getName(), connParams);
                        logger.info("jdbc connection added: " + conn.getName());
                    } else {
                        DAOImpl.connMap.put(conn.getName(), conn
                                .getDatasource());
                        logger.info("ds connection added: " + conn.getName());
                    }
                }
            }
        }
    }

    private void addNamedQry(String name, String qry) {
        if (name != null && qry != null) {
            qry = qry.trim();
            ConfigParser.NAMED_QUERY_MAPPING.put(name, qry);
            logger.debug("query name '" + name + "' = '" + qry + "'");
        }
    }

    private void filterComments() {
        List l = new ArrayList();
        List indented = new ArrayList();
        for (int i = 0; i < lines.size(); i++) {
            String str = lines.get(i).toString();
            logger.trace("(" + i + ") " + str);
            int index = str.indexOf("#");
            if (index > 1) {
                l.add(str.substring(0, index).trim());
                indented.add(indents.get(i));
            } else if (index == -1) {
                l.add(str);
                indented.add(indents.get(i));
            }
        }
        lines = l;
        indents = indented;
    }

    private void parse() throws IOException {

    	System.out.println(System.getProperty("db.yaml.file"));
        String yaml = (System.getProperty("db.yaml.file") == null) ? file : System.getProperty("db.yaml.file");
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        InputStream is = cl.getResourceAsStream(yaml);
        if (!yaml.equals(file))
            is = new FileInputStream(yaml);
        int blen = is.available();
        blen++;
        byte[] bytes = new byte[blen];
        if (blen > 0)
            logger.debug("rishi configuration file is " + yaml);
        is.read(bytes);
        bytes[blen - 1] = '\n';

        StringBuffer line = new StringBuffer();
        int indented = 0;
        boolean txtStarted = false;
        int lineCount = 0;
        for (int i = 0; i < blen; i++) {
            char c = (char) bytes[i];
            // System.out.print(c);
            boolean isSpace = Character.isWhitespace(c);
            boolean isNewline = (c == '\n' || c == '\r');
            if (isNewline) {

                // ok we have a new line here
                lineCount++;

                // add non-empty lines
                if (line.toString().trim().length() > 0) {
                    String linestr = line.toString().trim();
                    lines.add(linestr);
                    indents.add(new Integer(indented));
                }
                txtStarted = false;
                indented = 0;
                line = new StringBuffer();
            } else {
                if (isSpace && !txtStarted)
                    indented++;
                else {
                    if (!txtStarted)
                        txtStarted = true;
                    if (c != '\t')
                        line.append(c);
                }
            }
        }
    }

    private void parseClassEntityHash() {
        int startAt = -1;
        boolean found = false;
        for (int i = 0; i < lines.size(); i++) {
            if (lines.get(i).equals("class-entity-mappings:")) {
                logger
                        .debug("starting class-entity-mappings at non-empty line#"
                                + i);
                startAt = i + 1;
                found = true;
            }
        }

        try {
            while (found) {

                String str;
                try {
                    str = lines.get(startAt).toString();
                } catch (Exception e) {
                    break;
                }

                found = str.indexOf("- ") == 0;
                if (found) {
                    startAt++;

                    String[] split = StringUtils.splitAtFirstColon(str);
                    if (split.length == 2) {
                        String cls = split[0].substring(str.indexOf("- ") + 2)
                                .trim();
                        String entity = split[1].trim();
                        logger.debug(cls + ".class maps to entity '" + entity
                                + "'");
                        DAOImpl.entityMap.put(cls, entity);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parseColMappings() {
        try {
            int startAt = 0;
            int endAt = 0;
            boolean colOk = false;
            for (int i = 0; i < lines.size(); i++) {
                String str = lines.get(i).toString();
                if (startAt == 0 && str.indexOf("column-mappings:") != -1) {
                    colOk = true;
                    startAt = i + 1;
                    endAt = i + 1;
                    logger.trace("OK!!! startAt = " + startAt);
                    // ConfigParser.datePattern = dp;

                } else if (startAt > 0
                        && (str.indexOf("pattern:") > 0
                        || str.indexOf("ed-queries") > 0
                        || str.indexOf("-mappings:") > 0 || str
                        .indexOf("nnections:") > 0))
                    break;
                else
                    endAt++;
            }
            logger.trace("OK!!! startAt = " + startAt + " endAt = " + endAt);
            while (colOk && (startAt < endAt)) {
                String str = lines.get(startAt++).toString();
                // logger.debug(str);
                String col = str.substring(str.indexOf(":") + 1, str.length())
                        .trim();
                String prop = str.substring(str.indexOf("- ") + 1,
                        str.indexOf(":")).trim();
                if (prop != null && col != null) {
                    int lastDot = prop.lastIndexOf(".");
                    logger.debug("Column mapping: " + prop + "=" + col);
                    String revCol = col.toLowerCase() + "."
                            + prop.substring(0, lastDot);
                    ConfigParser.COL_MAPPING.put(prop, col);
                    ConfigParser.COL_MAPPING.put(revCol, prop
                            .substring(lastDot + 1));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // logger.debug("--> " + ConfigParser.COL_MAPPING);

    }

    private void parseConnections() {
        int startAt = 0;
        int endAt = 0;
        boolean found;

        for (int i = 0; i < lines.size(); i++) {
            if (lines.get(i).equals("connections:")) {
                startAt = i;
                found = true;
                endAt = startAt;
            }
        }

        do {
            logger.trace("endAt=" + endAt);
            String str;
            try {
                str = lines.get(++endAt).toString();
            } catch (IndexOutOfBoundsException e) {
                break;
            }
            found = false;
            for (int j = 0; j < cnAttribs.length; j++) {
                if (str.indexOf(cnAttribs[j]) != -1) {
                    found = true;
                    // endAt++;
                    break;
                }
            }
        } while (found);

        logger.trace("parsing connections on non-empty lines#" + startAt + "-"
                + (endAt));

        ConnectionData ch = null;
        for (int i = startAt; i < endAt; i++) {
            String str = lines.get(i).toString();

            if (str.indexOf("-") == 0) {
                ch = new ConnectionData();
                holders.add(ch);
            }

            logger.trace(str + " ch=" + ch);

            String[] split = StringUtils.splitAtFirstColon(str);
            if (split.length == 2 && split[1] != null) {

                for (int j = 0; j < cnAttribs.length; j++) {
                    if (split[0].indexOf(cnAttribs[j]) != -1) {
                        String value = split[1].trim();
                        BeanUtils.setValue(cnAttribs[j], ch, value);
                    }
                }
            }
        }

        addConnections();

    }

    private void parseDatePatterns() {
        for (int i = 0; i < lines.size(); i++) {
            String str = lines.get(i).toString();
            if (str.indexOf("date-pattern:") != -1) {
                String dp = str.substring(str.indexOf(":") + 1, str.length())
                        .trim();
                logger.info("date format pattern for java.sql.Date objects: "
                        + dp);
                ConfigParser.datePattern = dp;

            } else if (str.indexOf("timestamp-pattern:") != -1) {
                String tsp = str.substring(str.indexOf(":") + 1, str.length())
                        .trim();
                logger
                        .info("date format pattern for java.sql.Timestamp objects: "
                                + tsp);

                ConfigParser.tsPattern = tsp;
            }
        }
    }

    private void parseNamedQueries() {
        int startAt = -1;
        int endAt = -1;
        for (int i = 0; i < lines.size(); i++) {
            String str = lines.get(i).toString();
            if (str.indexOf("named-queries:") == 0) {
                startAt = i + 1;
                endAt = startAt;
            } else if (startAt > 0
                    && (str.indexOf("pattern:") > 0
                    || str.indexOf("-mappings:") > 0 || str
                    .indexOf("nnections:") > 0)) {
                break;
            } else {
                endAt++;
            }

        }
        if (startAt > 0) {
            logger.debug("loading named queries from line#" + startAt + "-"
                    + endAt);

            String name = null;
            String qry = null;

            for (int i = startAt; i < endAt; i++) {
                String str = lines.get(i).toString();
                if (str.indexOf("- ") == 0) {

                    addNamedQry(name, qry);

                    name = str.substring(2, str.indexOf(":")).trim();
                    qry = "";

                    if ((str.indexOf(">") > str.indexOf(":"))
                            || (str.indexOf("|") > str.indexOf(":"))) {
                        logger.debug("block for query name '" + name + "'");
                    } else {
                        qry = str.substring(str.indexOf(":") + 1).trim();
                        addNamedQry(name, qry);
                        name = null;
                        qry = null;
                    }
                } else if (name != null) {
                    qry += str + ' ';
                    // logger.debug(qry.trim());
                }
            }
            addNamedQry(name, qry);
        }
    }
}
